package com.wsc.crawler.DNS;

import java.net.InetAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.xbill.DNS.Address;

import com.wsc.crawler.URLutils.URLBean;
import com.wsc.crawler.grabber.URLBeanQueue;

public class AsyncDNSResolver {

	public   List<URLBeanQueue> smallqueues = new ArrayList<URLBeanQueue>();
	
	private  URLBeanQueue smallqueue = new URLBeanQueue();
	private  URLBeanQueue fullqueue;
	private  DNSResolver s;
	private   URLBeanQueue resolvedHosts;
	private  URLBeanQueue unresolvedHosts;
	
	public URLBeanQueue getFullqueue() {
		return fullqueue;
	}

	public void setFullqueue(URLBeanQueue fullqueue) {
		System.out.println("FullQueue is Set :"+fullqueue.size());
		this.fullqueue = fullqueue;
		System.out.println("FullQueue is Set :"+this.fullqueue.size());

	}

	/*
	 * Split full queue to 10 equal num of small queues
	 */
	private void splitQueueToSmall() {
		int splitsize = 0;
		int queuesize = fullqueue.size();
		System.out.println(queuesize);
		if (queuesize <= 10) {
			// queue size is less than split size, then add fullqueue to
			// smallqueues list.
			// Start one thread to resolve Dns
			System.out.println("queue size is less than 10, creating single thread to resolve hosts");
			smallqueues.add(fullqueue);
			System.out.println("Small queus size is: "+smallqueues.size());
		} else {
			int extra = queuesize % 10;
			// roundup the value, and split
			splitsize = (queuesize - extra) / 10;
			int startIndex = 0;
			int temp = splitsize;
			System.out.println("Split size :" + splitsize);
			// adding a method in URLQueue to get subqueue based on start index
			// and end index.
			while (splitsize <= queuesize - extra) {
				smallqueue = fullqueue.getSubQueue(startIndex, splitsize);
				System.out.println(startIndex + "  " + splitsize);
				startIndex = splitsize;
				splitsize = startIndex + temp;

				smallqueues.add(smallqueue);
				System.out.println(smallqueue.toString());
			}
			smallqueue = fullqueue.getSubQueue(queuesize - extra, queuesize);
			smallqueues.add(smallqueue);
			System.out.println("number of treads to be started :"
					+ smallqueues.size());
		}

	}

	// grabber calls this method to get list of resolved URLBeans.

	public URLBeanQueue getResolvedURLBeans() {
	 
		return this.resolvedHosts;
	}
	

	public URLBeanQueue getUnResolvedURLBeans() {
	 
		return this.unresolvedHosts;
	}

	public void initializeThreads() {

		splitQueueToSmall();
		
		List<DNSResolver> list = new ArrayList<DNSResolver>();
		long start = (new Date()).getTime();
		for (int i = 0; i < smallqueues.size(); i++) {

			s = new DNSResolver(smallqueues.get(i));
			list.add(s);
			s.start();
		}
		for (DNSResolver st : list) {
			try {
				st.join();
			} catch (Exception ex) {
			}
		}

		long end = (new Date()).getTime();
		System.out.println("Time taken to resolve hosts is: "
				+ (float) (end - start) / 1000 + "sec Num of Hosts: "
				+ fullqueue.size());
		resolvedHosts=s.getResolvedUrls();
		System.out
				.println("Total Resolved Hosts " + resolvedHosts.size());
		unresolvedHosts =s.getUnResolvedUrls();
		System.out.println("Total unResolved Hosts "
				+ unresolvedHosts.size());
		//s = null;
	}
	
	/*
	 * nullify all objects which are used in this class.
	 */
	public void nullify(){
		this.fullqueue=null;
		this.s=null;
		this.smallqueue=null;
		this.smallqueues=null;
	}
}



/*
 *  The DNSResolver class runs as a thread and resolves given list of unresolved hosts, and
 *  stores the details of the resolved host in a URLBean class, 
 *  and all beans are added to a list, finally all this lists, which are resolved by each thread
 *  are added to the static list.
 */
class DNSResolver extends Thread {
	// this is a static filed which can be accessed by AsyncDNSResolver class.
	// which consists of all resolved DNS Hosts.
	public URLBeanQueue resolvedHosts = new URLBeanQueue();
	public URLBeanQueue unresolvedHosts = new URLBeanQueue();

	private URLBeanQueue queue;
	private URL url;
	private URLBean bean;
	public DNSResolver(URLBeanQueue queue) {
		this.queue = queue;
		System.out.println("queue size :" + this.queue.size());
	}

	/* this method is called when we start a thread, and which resolves the hosts and creates a 
	 * bean with resolved data and adds the bean to a list.
	 * 
	 * (non-Javadoc)
	 * @see java.lang.Thread#run()
	 */
	public void run() {

		while(!queue.isEmpty()) {
			try {

				System.out.println("queue size is :" + queue.size());
				// get original url from frontier
				url =this.queue.dequeue().getOriginalURL();
				bean = new URLBean();
				bean.setOriginalURL(url);
				InetAddress addr = Address.getByName(url.getHost());
				// construct url by replacing host part with original ip
				url = new URL(url.toString().replaceFirst(url.getHost(), addr.getHostAddress()));
				bean.setDnsresolvedHost(url); 
				resolvedHosts.enqueue(bean);
				System.out.println("Bean is "+bean.toString());
			//	bean=null;
			} catch (Exception ex) {

				bean.setMessage(ex.getMessage());
				
				unresolvedHosts.enqueue(bean);
			//	bean = null;
				System.err.println(ex + " :" + url.getHost());
			}
		}
		
		this.queue = null;
	}

	/*
	 * return the all assembled DNS resolved hosts.
	 */
	public URLBeanQueue getResolvedUrls() {
	 
		return resolvedHosts;
	}
	/*
	 * return the unresolved hosts
	 */
	public URLBeanQueue getUnResolvedUrls() {
		return unresolvedHosts;
	}

	/* finally, nullify all objects used in this class.
	 * and be care full while calling this method because,
	 * if we call this method, before completion of the object use, then it'll
	 * make all objects null , so the data which is available in the following objects are no longer
	 * available.
	 * 
	 */
	public void nullifyall(){
		this.queue = null;
		this.bean = null;
		this.url = null;
		resolvedHosts  = null;
		unresolvedHosts  = null;
	}

 
}


